<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<title></title>
		<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>-->
        <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
		<style type="text/css">
			html,
			body {
			 width: 100%;
				height: 100%;
				margin: 0;
				padding: 0;
				overflow: hidden;
			}

			.container {
				width: 100%;
				height: 100%;
				margin: 0;
				padding: 0;
				/* background-color: #000000; */
				background: url('img/b87198c5d8baa1f22efb6818f4e298f6.jpeg');
				background-size: cover;
				background-repeat: no-repeat;
				background-position: center;
			}
		</style>
	</head>
	<body>
		<div id="jsi-cherry-container" class="container">
			<!-- <canvas class="canvas"></canvas> -->
		</div>

		<script>
			// function $(dom){
			// 	return document.querySelector(dom)
			// }
			var RENDERER = {
				INIT_CHERRY_BLOSSOM_COUNT: 30,
				MAX_ADDING_INTERVAL: 10,
				WATCH_INTERVAL: 300,

				init: function() {
					this.setParameters();
					this.reconstructMethods();
					this.setup();
					this.bindEvent();
					this.render();
				},
				setParameters: function() {
					this.$window = $(window);
					this.$container = $('#jsi-cherry-container');
					this.$canvas = $('<canvas />');
					this.context = this.$canvas.appendTo(this.$container).get(0).getContext('2d');
					this.cherries = [];
					this.watchIds = [];
				},
				reconstructMethods: function() {
					this.watchWindowSize = this.watchWindowSize.bind(this);
					this.jdugeToStopResize = this.jdugeToStopResize.bind(this);
					this.render = this.render.bind(this);
				},
				setup: function() {
					this.cherries.length = 0;
					this.watchIds.length = 0;
					this.width = this.$container.width();
					this.height = this.$container.height();
					this.$canvas.attr({
						width: this.width,
						height: this.height
					});
					this.maxAddingInterval = Math.round(this.MAX_ADDING_INTERVAL * 1000 / this.width);
					this.addingInterval = this.maxAddingInterval;
					this.createCherries();
				},
				createCherries: function() {
					for (var i = 0, length = Math.round(this.INIT_CHERRY_BLOSSOM_COUNT * this.width / 1000); i <
						length; i++) {
						this.cherries.push(new CHERRY_BLOSSOM(this, true));
					}
				},
				watchWindowSize: function() {
					this.clearTimer();
					this.tmpWidth = this.$window.width();
					this.tmpHeight = this.$window.height();
					this.watchIds.push(setTimeout(this.jdugeToStopResize, this.WATCH_INTERVAL));
				},
				clearTimer: function() {
					while (this.watchIds.length > 0) {
						clearTimeout(this.watchIds.pop());
					}
				},
				jdugeToStopResize: function() {
					var width = this.$window.width(),
						height = this.$window.height(),
						stopped = (width == this.tmpWidth && height == this.tmpHeight);

					this.tmpWidth = width;
					this.tmpHeight = height;

					if (stopped) {
						this.setup();
					}
				},
				bindEvent: function() {
					this.$window.on('resize', this.watchWindowSize);
				},
				render: function() {
					requestAnimationFrame(this.render);
					this.context.clearRect(0, 0, this.width, this.height);

					this.cherries.sort(function(cherry1, cherry2) {
						return cherry1.z - cherry2.z;
					});
					for (var i = this.cherries.length - 1; i >= 0; i--) {
						if (!this.cherries[i].render(this.context)) {
							this.cherries.splice(i, 1);
						}
					}
					if (--this.addingInterval == 0) {
						this.addingInterval = this.maxAddingInterval;
						this.cherries.push(new CHERRY_BLOSSOM(this, false));
					}
				}
			};
			var CHERRY_BLOSSOM = function(renderer, isRandom) {
				this.renderer = renderer;
				this.init(isRandom);
			};
			CHERRY_BLOSSOM.prototype = {
				FOCUS_POSITION: 300,
				FAR_LIMIT: 600,
				MAX_RIPPLE_COUNT: 100,
				RIPPLE_RADIUS: 100,
				SURFACE_RATE: 0.5,
				SINK_OFFSET: 20,

				init: function(isRandom) {
					this.x = this.getRandomValue(-this.renderer.width, this.renderer.width);
					this.y = isRandom ? this.getRandomValue(0, this.renderer.height) : this.renderer.height * 1.5;
					this.z = this.getRandomValue(0, this.FAR_LIMIT);
					this.vx = this.getRandomValue(-2, 2);
					this.vy = -2;
					this.theta = this.getRandomValue(0, Math.PI * 2);
					this.phi = this.getRandomValue(0, Math.PI * 2);
					this.psi = 0;
					this.dpsi = this.getRandomValue(Math.PI / 600, Math.PI / 300);
					this.opacity = 0;
					this.endTheta = false;
					this.endPhi = false;
					this.rippleCount = 0;

					var axis = this.getAxis(),
						theta = this.theta + Math.ceil(-(this.y + this.renderer.height * this.SURFACE_RATE) / this.vy) *
						Math.PI / 500;
					theta %= Math.PI * 2;

					this.offsetY = 40 * ((theta <= Math.PI / 2 || theta >= Math.PI * 3 / 2) ? -1 : 1);
					this.thresholdY = this.renderer.height / 2 + this.renderer.height * this.SURFACE_RATE * axis.rate;
					this.entityColor = this.renderer.context.createRadialGradient(0, 40, 0, 0, 40, 80);
					this.entityColor.addColorStop(0, 'hsl(330, 70%, ' + 50 * (0.3 + axis.rate) + '%)');
					this.entityColor.addColorStop(0.05, 'hsl(330, 40%,' + 55 * (0.3 + axis.rate) + '%)');
					this.entityColor.addColorStop(1, 'hsl(330, 20%, ' + 70 * (0.3 + axis.rate) + '%)');
					this.shadowColor = this.renderer.context.createRadialGradient(0, 40, 0, 0, 40, 80);
					this.shadowColor.addColorStop(0, 'hsl(330, 40%, ' + 30 * (0.3 + axis.rate) + '%)');
					this.shadowColor.addColorStop(0.05, 'hsl(330, 40%,' + 30 * (0.3 + axis.rate) + '%)');
					this.shadowColor.addColorStop(1, 'hsl(330, 20%, ' + 40 * (0.3 + axis.rate) + '%)');
				},
				getRandomValue: function(min, max) {
					return min + (max - min) * Math.random();
				},
				getAxis: function() {
					var rate = this.FOCUS_POSITION / (this.z + this.FOCUS_POSITION),
						x = this.renderer.width / 2 + this.x * rate,
						y = this.renderer.height / 2 - this.y * rate;
					return {
						rate: rate,
						x: x,
						y: y
					};
				},
				renderCherry: function(context, axis) {
					context.beginPath();
					context.moveTo(0, 40);
					context.bezierCurveTo(-60, 20, -10, -60, 0, -20);
					context.bezierCurveTo(10, -60, 60, 20, 0, 40);
					context.fill();

					for (var i = -4; i < 4; i++) {
						context.beginPath();
						context.moveTo(0, 40);
						context.quadraticCurveTo(i * 12, 10, i * 4, -24 + Math.abs(i) * 2);
						context.stroke();
					}
				},
				render: function(context) {
					var axis = this.getAxis();

					if (axis.y == this.thresholdY && this.rippleCount < this.MAX_RIPPLE_COUNT) {
						context.save();
						context.lineWidth = 2;
						context.strokeStyle = 'hsla(0, 0%, 100%, ' + (this.MAX_RIPPLE_COUNT - this.rippleCount) / this
							.MAX_RIPPLE_COUNT + ')';
						context.translate(axis.x + this.offsetY * axis.rate * (this.theta <= Math.PI ? -1 : 1), axis.y);
						context.scale(1, 0.3);
						context.beginPath();
						context.arc(0, 0, this.rippleCount / this.MAX_RIPPLE_COUNT * this.RIPPLE_RADIUS * axis.rate, 0,
							Math.PI * 2, false);
						context.stroke();
						context.restore();
						this.rippleCount++;
					}
					if (axis.y < this.thresholdY || (!this.endTheta || !this.endPhi)) {
						if (this.y <= 0) {
							this.opacity = Math.min(this.opacity + 0.01, 1);
						}
						context.save();
						context.globalAlpha = this.opacity;
						context.fillStyle = this.shadowColor;
						context.strokeStyle = 'hsl(330, 30%,' + 40 * (0.3 + axis.rate) + '%)';
						context.translate(axis.x, Math.max(axis.y, this.thresholdY + this.thresholdY - axis.y));
						context.rotate(Math.PI - this.theta);
						context.scale(axis.rate * -Math.sin(this.phi), axis.rate);
						context.translate(0, this.offsetY);
						this.renderCherry(context, axis);
						context.restore();
					}
					context.save();
					context.fillStyle = this.entityColor;
					context.strokeStyle = 'hsl(330, 40%,' + 70 * (0.3 + axis.rate) + '%)';
					context.translate(axis.x, axis.y + Math.abs(this.SINK_OFFSET * Math.sin(this.psi) * axis.rate));
					context.rotate(this.theta);
					context.scale(axis.rate * Math.sin(this.phi), axis.rate);
					context.translate(0, this.offsetY);
					this.renderCherry(context, axis);
					context.restore();

					if (this.y <= -this.renderer.height / 4) {
						if (!this.endTheta) {
							for (var theta = Math.PI / 2, end = Math.PI * 3 / 2; theta <= end; theta += Math.PI) {
								if (this.theta < theta && this.theta + Math.PI / 200 > theta) {
									this.theta = theta;
									this.endTheta = true;
									break;
								}
							}
						}
						if (!this.endPhi) {
							for (var phi = Math.PI / 8, end = Math.PI * 7 / 8; phi <= end; phi += Math.PI * 3 / 4) {
								if (this.phi < phi && this.phi + Math.PI / 200 > phi) {
									this.phi = Math.PI / 8;
									this.endPhi = true;
									break;
								}
							}
						}
					}
					if (!this.endTheta) {
						if (axis.y == this.thresholdY) {
							this.theta += Math.PI / 200 * ((this.theta < Math.PI / 2 || (this.theta >= Math.PI && this
								.theta < Math.PI * 3 / 2)) ? 1 : -1);
						} else {
							this.theta += Math.PI / 500;
						}
						this.theta %= Math.PI * 2;
					}
					if (this.endPhi) {
						if (this.rippleCount == this.MAX_RIPPLE_COUNT) {
							this.psi += this.dpsi;
							this.psi %= Math.PI * 2;
						}
					} else {
						this.phi += Math.PI / ((axis.y == this.thresholdY) ? 200 : 500);
						this.phi %= Math.PI;
					}
					if (this.y <= -this.renderer.height * this.SURFACE_RATE) {
						this.x += 2;
						this.y = -this.renderer.height * this.SURFACE_RATE;
					} else {
						this.x += this.vx;
						this.y += this.vy;
					}
					return this.z > -this.FOCUS_POSITION && this.z < this.FAR_LIMIT && this.x < this.renderer.width *
					1.5;
				}
			};
			// $(function() {
				RENDERER.init();
			// });
		</script>
	</body>
</html>
